package db

import (
	"errors"
	"gitee.com/yysf_xin/go-common/crypto"
	"gitee.com/yysf_xin/go-common/tools"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"net/http"
	"reflect"
	"strconv"
	"sync"
)

type Client struct {
	*gorm.DB
	Cfg    *Config
	Cipher *crypto.AesCipher
	sync.Mutex
}

func (c *Client) String() string {
	return "DB"
}

type CipherPlugin struct {
	*crypto.AesCipher
}

func (c *CipherPlugin) Name() string {
	return "cipher"
}

func (c *CipherPlugin) Initialize(_ *gorm.DB) error {
	return nil
}

func NewClient(cfg *Config, config *gorm.Config) (c *Client, err error) {
	c = &Client{
		Cfg: cfg,
	}

	if cfg.DbName == "" {
		cfg.DbName = "app.db"
		cfg.Upgrade = true
	}

	tools.Log.Println("open", cfg.DbName, cfg.Upgrade)

	if config == nil {
		config = &gorm.Config{
			DisableForeignKeyConstraintWhenMigrating: true,
			DisableNestedTransaction:                 true,
			Logger:                                   logger.Default.LogMode(logger.LogLevel(cfg.LogLevel)),
		}
	}

	db := OpenDB(cfg, config)
	if db == nil {
		return nil, errors.New("not support db name " + cfg.DbName)
	}

	// gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=muxMap

	c.DB, err = gorm.Open(db, config)

	if err != nil {
		tools.Log.Println(err)
	} else if cfg.AesKey != "" {
		c.Cipher, err = crypto.NewAesCipher(cfg.AesKey)
		if c.Cipher != nil {
			err = c.Use(&CipherPlugin{
				c.Cipher,
			})
		}
	}
	return
}
func Paginate(r *http.Request) func(db *gorm.DB) *gorm.DB {
	return PaginateKey(r, "current", "pageSize")
}
func PaginateKey(r *http.Request, current, pageSize string) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		query := r.URL.Query()

		page, _ := strconv.Atoi(query.Get(current))
		if page == 0 {
			page = 1
		}
		pageSize, _ := strconv.Atoi(query.Get(pageSize))
		switch {
		case pageSize > 100:
			pageSize = 100
		case pageSize <= 0:
			pageSize = 10
		}

		offset := (page - 1) * pageSize
		return db.Offset(offset).Limit(pageSize)
	}
}

func (c *Client) UpgradeDB(models interface{}) (err error) {
	var values = reflect.ValueOf(models).Elem()
	typeArr := make([]interface{}, values.NumField())
	for i := 0; i < values.NumField(); i++ {
		typeArr[i] = values.Field(i).Interface()
	}
	tools.Log.Println("start upgrade db..")
	err = c.AutoMigrate(typeArr...)
	return
}
